package user

import (
	"bytes"
	"cross-shard-channel/common"
	"cross-shard-channel/ledger"
	"encoding/json"
	"github.com/eywa-protocol/bls-crypto/bls"
	"io/ioutil"
	"net/http"
	"time"
)

// 定义发出请求的函数
// advIP = "http://localhost:8000"
func InitOpenRequest(client *http.Client, advIP string, cid int, ourBalance int, advBalance int, life int) bool {
	// 基本内容
	var responseObject InitOpenResponse
	var err error
	name := "/init-open"
	url := advIP + name

	// 构造body
	UN.Lock()
	inputUTXOA := common.UTXO{
		UN.Pub,
		[]byte("0"),
		ourBalance,
	}
	UN.Unlock()

	myChPri, myChPub := bls.GenerateRandomKey()
	body := InitOpen{
		cid,
		inputUTXOA,
		myChPub,
		advBalance,
		life,
	}

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(body)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 计算最终通道押金的UTXO
	inputUTXOs := []common.UTXO{inputUTXOA, responseObject.InputUTXOB}
	createUTXO := common.UTXO{
		responseObject.ChannelPubFinal,
		nil,
		ourBalance + advBalance,
	}
	outputUTXOs := []common.UTXO{createUTXO}
	createTx := common.GenerateTransactionByUTXO(inputUTXOs, outputUTXOs, 0)
	createUTXO.Tx_id = createTx.TxId[:]

	// 记录通道信息
	UN.Lock()
	UN.AddNewChannelToNode(cid, responseObject.InputUTXOB.PublicKey, advIP, ourBalance, advBalance, life)
	TarChannel := UN.Channels[cid]
	UN.Unlock()

	TarChannel.Lock()
	TarChannel.Type = 0
	TarChannel.MyBalance = ourBalance
	TarChannel.AdvBalance = advBalance
	TarChannel.InputCreateUTXOA = inputUTXOA
	TarChannel.InputCreateUTXOB = responseObject.InputUTXOB
	TarChannel.MyChPri = myChPri
	TarChannel.MyChPub = myChPub
	TarChannel.AdvChPub = responseObject.ChannelPubB
	TarChannel.ChannelPub = responseObject.ChannelPubFinal
	TarChannel.CreateUTXO = createUTXO
	TarChannel.TxCreate = *createTx
	TarChannel.Unlock()

	return true
}

func OpenNotifyRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject OpenNotify
	var responseObject OpenNotifyResponse
	name := "/open-notify"

	// 获取本地信息
	UN.Lock()
	myPub := UN.Pub
	myPri := UN.Pri
	UN.Unlock()

	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advPub := ch.Adv
	advIP := ch.AdvIP
	ch.Unlock()

	myNotifyPri, myNotifyPub := GenerateKeyWithPub(advPub)
	notifyPubB := advPub.Aggregate(myNotifyPub)

	// 构建给对方的通知交易
	notifyOutputB := common.UTXO{
		notifyPubB,
		nil,
		0,
	} // 这个实际上是给Adv的通知
	notifyInputB := common.UTXO{
		myPub,
		[]byte("0"),
		0,
	}
	TxNotifyB := common.GenerateTransactionByUTXO([]common.UTXO{notifyInputB}, []common.UTXO{notifyOutputB}, 0)
	NotifyBSig := TxNotifyB.Sign(myPri)
	TxNotifyB.Sigs = []bls.Signature{NotifyBSig}

	// 构建request object
	requestObject.Cid = cid
	requestObject.TxNotify = *TxNotifyB

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 对通知交易的获取与处理
	txRes1 := CallLedgerTransactionExe(requestObject.TxNotify, "tx_notifyA")
	if !txRes1 {
		println("ERROR: ledger refuse transaction notifyA")
		return false
	}

	txRes2 := CallLedgerTransactionExe(responseObject.TxNotify, "tx_notifyB")
	if !txRes2 {
		println("ERROR: ledger refuse transaction notifyB")
		return false
	}
	// 模拟区块链共识延迟
	time.Sleep(time.Duration(ledger.TX_DELAY) * time.Millisecond)

	// 修改状态信息
	ch.Lock()
	ch.MyNotifyPri = myNotifyPri
	ch.MyNotifyPub = myNotifyPub
	ch.NotifyUTXOB = requestObject.TxNotify.Output[0]
	ch.NotifyUTXOA = responseObject.TxNotify.Output[0]
	ch.Unlock()

	//println("client open notify success")
	return true
}

func OpenAccountRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject OpenAccount
	var responseObject OpenAccountResponse
	name := "/open-account"

	// 获取本地信息
	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP
	advChPub := ch.AdvChPub
	ch.Unlock()

	// 新建一个公私钥对，用于构建对方的锁定账户
	lockPriForMe, lockPubForMe := GenerateKeyWithPub(advChPub)

	// 构建request object
	requestObject.Cid = cid
	requestObject.LockPub = lockPubForMe

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	ch.Lock()
	ch.AdvLockPubs = append(ch.AdvLockPubs, responseObject.LockPub)
	ch.MyLockPubs = append(ch.MyLockPubs, lockPubForMe)
	ch.MyLockPris = append(ch.MyLockPris, lockPriForMe)
	ch.Unlock()

	return true
}

func OpenUnlockRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject OpenUnlock
	var responseObject OpenUnlockResponse
	name := "/open-unlock"

	// 获取本地信息
	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP

	advMainPub := ch.Adv
	myChPub := ch.MyChPub
	myChPri := ch.MyChPri

	myChBalance := ch.MyBalance
	advChBalance := ch.AdvBalance

	createUTXO := ch.CreateUTXO
	notifyUTXOA := ch.NotifyUTXOA
	notifyUTXOB := ch.NotifyUTXOB

	lockTime := ch.LifeTime

	// 账户锁定信息
	advLockPub := ch.AdvLockPubs[0]
	myLockPri := ch.MyLockPris[0]
	ch.Unlock()

	// 构建对方的锁定账户
	lockPubB := myChPub.Aggregate(advLockPub)

	// commit 交易构造，这个交易是给对方的
	commitInputs := []common.UTXO{createUTXO, notifyUTXOA, notifyUTXOB}
	commitOutputA := common.UTXO{
		myChPub,
		nil,
		myChBalance,
	}
	commitOutputB := common.UTXO{
		lockPubB,
		nil,
		advChBalance,
	}
	commitOutputs := []common.UTXO{commitOutputB, commitOutputA}
	commitTx := common.GenerateTransactionByUTXO(commitInputs, commitOutputs, 0)
	commitOutputB.Tx_id = commitTx.TxId

	// 构造锁定交易
	unlockInputs := []common.UTXO{commitOutputB}
	unlockOutputUTXO := common.UTXO{
		advMainPub,
		nil,
		commitOutputB.Balance,
	}
	unlockOutputs := []common.UTXO{unlockOutputUTXO}
	unlockTx := common.GenerateTransactionByUTXO(unlockInputs, unlockOutputs, lockTime)
	unlockSigA := unlockTx.Sign(myChPri)

	// 构建request object
	requestObject.Cid = cid
	requestObject.TxCommitB = *commitTx
	requestObject.TxUnlockB = *unlockTx
	requestObject.Sig = unlockSigA

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	url := advIP + name
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构造自己unlock交易的签名，方法是用自己的通道账户私钥签名再叠加对方提供的签名
	myLocKPrSig := responseObject.TxUnlockA.Sign(myLockPri)
	responseObject.TxUnlockA.Sigs = []bls.Signature{responseObject.Sig.Aggregate(myLocKPrSig)}

	if !responseObject.TxUnlockA.Verify() {
		println("ERROR! client open unlock fail")
		return false
	}

	// 修改状态信息
	ch.Lock()
	ch.TxUnlocks = []common.Transaction{}
	ch.TxCommits = []common.Transaction{}
	ch.AdvTxCommits = []common.Transaction{}

	ch.TxCommits = append(ch.TxCommits, responseObject.TxCommitA)
	ch.TxUnlocks = append(ch.TxUnlocks, responseObject.TxUnlockA)
	ch.AdvTxCommits = append(ch.AdvTxCommits, *commitTx)
	ch.Unlock()

	//println("Open Unlock Success")
	return true
}

func OpenCommitRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject OpenCommit
	var responseObject OpenCommitResponse
	name := "/open-commit"

	// 获取本地信息
	UN.Lock()
	myMainPub := UN.Pub
	myMainPri := UN.Pri
	UN.Unlock()

	ch := UN.GetChannelFromNode(cid)
	ch.Lock()
	advIP := ch.AdvIP
	url := advIP + name // 至此为必须部分

	myChPri := ch.MyChPri
	myNotifyPri := ch.MyNotifyPri

	advCommitTx := ch.AdvTxCommits[0]
	myCommitTx := ch.TxCommits[0]
	ch.Unlock()

	// 对commit交易进行签名
	requestObject.Cid = cid
	requestObject.MainSigA = advCommitTx.Sign(myMainPri)
	requestObject.ChSigA = advCommitTx.Sign(myChPri)
	requestObject.NotifySigA = advCommitTx.Sign(myNotifyPri)

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构造commit交易
	myMainSig := myCommitTx.Sign(myMainPri)
	myChSig := myCommitTx.Sign(myChPri)
	myNotifySig := myCommitTx.Sign(myNotifyPri)

	sig0 := myChSig.Aggregate(responseObject.ChSigB)
	sig1 := myMainSig.Aggregate(responseObject.NotifySigB)
	sig2 := responseObject.MainSigB.Aggregate(myNotifySig)
	myCommitTx.Sigs = []bls.Signature{sig0, sig1, sig2}

	// 构建退款交易
	refundInputs := []common.UTXO{advCommitTx.Output[1]}
	refundOutputs := []common.UTXO{common.UTXO{
		myMainPub,
		nil,
		advCommitTx.Output[1].Balance,
	}}
	myRefundTx := common.GenerateTransactionByUTXO(refundInputs, refundOutputs, 0)
	sigRefund := myRefundTx.Sign(myChPri)
	myRefundTx.Sigs = []bls.Signature{sigRefund}

	// 更新本地信息
	ch.Lock()
	ch.TxRefunds = []common.Transaction{}

	ch.TxCommits[0].Sigs = []bls.Signature{sig0, sig1, sig2}
	ch.TxRefunds = append(ch.TxRefunds, *myRefundTx)
	ch.Unlock()

	if myCommitTx.Verify() {
		//println("Open commit success")
	} else {
		println("Open commit fail")
	}

	return true
}

func OpenCreateRequest(client *http.Client, cid int) bool {
	// 初始化基本参数
	var requestObject OpenCreate
	var responseObject OpenCreateResponse
	name := "/open-create"

	// 获取本地消息
	UN.Lock()
	myMainPri := UN.Pri
	ch := UN.Channels[cid]
	UN.Unlock()

	ch.Lock()
	advIP := ch.AdvIP
	url := advIP + name // 至此为必须部分

	txCreate := ch.TxCreate
	ch.Unlock()

	sigA := txCreate.Sign(myMainPri)

	// 构建request
	requestObject.Cid = cid
	requestObject.SigA = sigA

	// 固定内容
	// generate request body
	bodyStr, err := json.Marshal(requestObject)
	if err != nil {
		println(err.Error())
		return false
	}
	bodyBuffer := bytes.NewBuffer(bodyStr)
	// do request
	request, _ := http.NewRequest("POST", url, bodyBuffer)
	request.Header.Set("Content-type", "application/json")
	response, err := client.Do(request)
	// error process
	if err != nil {
		println(err.Error())
		return false
	} else if response.StatusCode != 200 {
		println("response error")
		return false
	}

	// 模拟网络通信延迟
	time.Sleep(time.Duration(NET_DELAY) * time.Millisecond)

	// 解析回复
	responseBody, _ := ioutil.ReadAll(response.Body)
	err = json.Unmarshal(responseBody, &responseObject)
	if err != nil {
		println("response unmarshal error")
	}

	// 构建最终创建交易
	txCreate.Sigs = []bls.Signature{sigA, responseObject.SigB}
	if !txCreate.Verify() {
		println("Sig ERROR! open create fail")
	} else {
		//println("Open Create Success")
	}

	// 等待结果
	txRes := CallLedgerTransactionExe(txCreate, "tx_create")
	if !txRes {
		println("ERROR: ledger refuse transaction create")
		return false
	}

	// 模拟区块链共识延迟
	time.Sleep(time.Duration(ledger.TX_DELAY) * time.Millisecond)

	ch.Lock()
	ch.Version = 0
	ch.Unlock()

	return true
}
